package com.yjd.comm.borrow.util;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;
import com.yjd.comm.base.enums.DbWREnums;
import com.yjd.comm.borrow.dto.AdminMobileDto;
import com.yjd.comm.borrow.dto.BorrowCostDto;
import com.yjd.comm.borrow.dto.BorrowDto;
import com.yjd.comm.borrow.dto.CreditorTransferCounterDto;
import com.yjd.comm.borrow.dto.OverdueDto;
import com.yjd.comm.borrow.dto.OverdueDto.OverdueDtoCalcRquest;
import com.yjd.comm.borrow.dto.ReturnFeeDto;
import com.yjd.comm.borrow.dto.ReturnFeeDto.EveryReturnFeeDto;
import com.yjd.comm.borrow.enums.BorrowReturnOverdueAdvanceEnum;
import com.yjd.comm.borrow.enums.BorrowStatusEnum;
import com.yjd.comm.borrow.enums.CreditorStatusEnum;
import com.yjd.comm.borrow.enums.OverduePartEnum;
import com.yjd.comm.borrow.enums.ReturnStatusEnum;
import com.yjd.comm.borrow.model.ArrearsFormModel;
import com.yjd.comm.borrow.model.BorrowCostModel;
import com.yjd.comm.borrow.model.BorrowDataModel;
import com.yjd.comm.borrow.model.BorrowModel;
import com.yjd.comm.borrow.model.CreditorModel;
import com.yjd.comm.borrow.model.InvestModel;
import com.yjd.comm.borrow.model.ProfitFormModel;
import com.yjd.comm.system.enums.DictTypeEnum;
import com.yjd.comm.system.model.SysParamModel;
import com.yjd.comm.system.util.DictFieldModel;
import com.yjd.comm.system.util.SysParamUtil;
import com.yjd.comm.user.convert.UserDataModelConvert;
import com.yjd.comm.user.model.UserDataModel;
import com.yjd.comm.user.service.IUserDataService;
import com.yjd.comm.util.ArithUtil;
import com.yjd.comm.util.DateUtil;
import com.yjd.comm.util.FrameUtil;
import com.yjd.comm.util.StringUtil;
import com.yjd.comm.vo.Constant.CalcModeEnum;
import com.yjd.comm.vo.Constant.SysParamKeyEnum;

/**
 * <b>description</b>：借款项目工具类 <br>
 * <b>time</b>：2014-8-22 下午7:14:54 <br>
 * <b>author</b>： ready likun_557@163.com
 */
public class BorrowUtil {

	private static Logger logger = Logger.getLogger(BorrowUtil.class);

	/**
	 * 根据借款人id生成新地借款协议号
	 * 
	 * @param idUser
	 *            借款人id
	 * @return
	 */
	public static String getNewDealNumber(Long user_id) {
		return "YJD"
				+ DateUtil.format(Calendar.getInstance().getTime(),
						DateUtil.PATTERN_yyyyMMddHHmmssSSS) + user_id;

	}

	/**
	 * 是否开启邀请奖励
	 * 
	 * @return
	 * @throws Exception
	 */
	public static boolean ispromotion() throws Exception {
		List<SysParamModel> list = SysParamUtil
				.getSysParamModels(SysParamKeyEnum.ISPROMOTION);
		if (list != null && list.size() >= 1) {
			String value = list.get(0).getParam_value();
			if (value != null && "1".equals(value.trim())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 
	 * 获取理财金返现基数
	 * 
	 * @return
	 * @throws Exception
	 */
	public static double getFinancialPriceReturn() throws Exception {
		String value = SysParamUtil
				.getValue(SysParamKeyEnum.FINANCIAL_PRICE_RETURN);
		return StringUtil.isNotEmpty(value) ? Double.parseDouble(value) : 20000;
	}

	/**
	 * 获取提前还款违约金费率
	 * 
	 * @return
	 * @throws Exception
	 */
	public static double getPreRepayRineRate() throws Exception {
		return Double.parseDouble(SysParamUtil
				.getValue(SysParamKeyEnum.PRE_REPAY_FINE_RATE));
	}

	/**
	 * 获取理财金每次返现金额
	 * 
	 * @return
	 * @throws Exception
	 */
	public static double getFinancialPriceReturnEvery() throws Exception {
		String value = SysParamUtil
				.getValue(SysParamKeyEnum.FINANCIAL_PRICE_RETURN_EVERY);
		return StringUtil.isNotEmpty(value) ? Double.parseDouble(value) : 100;
	}

	/**
	 * 是否开启了推广奖励
	 * 
	 * @return
	 * @throws Exception
	 */
	public static boolean isextension() throws Exception {
		List<SysParamModel> list = SysParamUtil
				.getSysParamModels(SysParamKeyEnum.ISEXTENSION);
		if (list != null && list.size() >= 1) {
			String value = list.get(0).getParam_value();
			if (value != null && "1".equals(value.trim())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 根据贷款周期获取项目的服务费费率(百分比)
	 * 
	 * @param limit
	 *            贷款周期（月）
	 * @return
	 * @throws Exception
	 */
	public static Double getServicesFee(Integer limit) throws Exception {
		return SysParamUtil.getParamValueByRegIn(
				SysParamKeyEnum.PROJECT_SERVICES_FEE,
				Double.valueOf(limit.toString()));
	}

	/**
	 * 获取借款管理费费率
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Double getManageFee() throws Exception {
		SysParamModel paramModel = SysParamUtil.getSysParamModels(
				SysParamKeyEnum.PROJECT_MANAGE_FEE).get(0);
		return Double.valueOf(paramModel.getParam_value());
	}

	/**
	 * 根据逾期天数获取逾期罚息费率(百分比)
	 * 
	 * @param overdueDay
	 *            逾期天数
	 * @return
	 * @throws Exception
	 */
	public static Double getOverdueFinePriceFee(Integer overdueDay)
			throws Exception {
		return SysParamUtil.getParamValueByRegIn(
				SysParamKeyEnum.OVERDUE_FINE_PRICE_FEE,
				Double.valueOf(overdueDay.toString()));
	}

	/**
	 * 根据逾期天数获取逾期管理费费率(百分比)
	 * 
	 * @param overdueDay
	 *            逾期天数
	 * @return
	 * @throws Exception
	 */
	public static Double getOverdueFineManageFee(Integer overdueDay)
			throws Exception {
		return SysParamUtil.getParamValueByRegIn(
				SysParamKeyEnum.OVERDUE_FINE_MANAGE_FEE,
				Double.valueOf(overdueDay.toString()));
	}

	/**
	 * 获取每份投标金额
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Double getInvestEachAmount() throws Exception {
		List<SysParamModel> list = SysParamUtil
				.getSysParamModels(SysParamKeyEnum.INVEST_EACH_AMOUNT);
		if (list != null && list.size() >= 1) {
			SysParamModel paramModel = list.get(0);
			return Double.valueOf(paramModel.getParam_value());
		} else {
			return 100D;
		}
	}

	/**
	 * 逾期是否垫付
	 * 
	 * @return
	 * @throws Exception
	 */
	public static boolean overdueIsAdvance() throws Exception {
		return "1".equals(SysParamUtil
				.getSysParamModels(SysParamKeyEnum.OVERDUE_IS_ADVANCE).get(0)
				.getParam_value());
	}

	/**
	 * 获取还款日期
	 * 
	 * @param term
	 *            第几期
	 * @param nowday
	 *            当前时间
	 * @return
	 */
	public static long countRepayDay(int term, long nowday) {
		// 首先是我需要的年月日 如果不是 则取这个年月的最后一天 主要集中在 29 30 31
		Date tempNowData = DateUtil.parseDate(DateUtil.format(
				DateUtil.timestampToDate(nowday), DateUtil.PATTERN_yyyyMMdd),
				DateUtil.PATTERN_yyyyMMdd);
		Date newdata = DateUtil.addMonth(tempNowData, term);
		return FrameUtil.getTime(newdata);
	}

	/**
	 * 构建借款款项信息
	 * 
	 * @param borrowDto
	 *            借款项目对象
	 * @return
	 * @throws Exception
	 */
	public static BorrowCostDto buildBorrowCostDto(BorrowDto borrowDto,
			Long curTime) throws Exception {
		BorrowCostDto bs = new BorrowCostDto();
		bs.setCapital(borrowDto.getBorrowModel().getPrice());
		bs.setInterest(FeeCalcUtil.count(
				CalcModeEnum.getCalcModeEnum(borrowDto.getBorrowModel()
						.getMode()), borrowDto.getBorrowModel().getPrice(),
				borrowDto.getBorrowModel().getApr(),
				borrowDto.getBorrowModel().getLimit(), curTime).getInterest());
		bs.setLimit(borrowDto.getBorrowModel().getLimit());
		bs.setServiceFee(FeeCalcUtil.round(ArithUtil.muls(0.01, borrowDto
				.getBorrowModel().getPrice(), BorrowUtil
				.getServicesFee(borrowDto.getBorrowModel().getLimit()))));
		bs.setManageFee(FeeCalcUtil.round(ArithUtil.muls(0.01, borrowDto
				.getBorrowModel().getPrice(), BorrowUtil.getManageFee())));
		bs.setTotalPrice(FeeCalcUtil.round(ArithUtil.adds(bs.getCapital(),
				bs.getInterest())));
		return bs;
	}

	/**
	 * 根据借款对象构建借款费用信息
	 * 
	 * @param borrowModel
	 *            借款对象
	 * @return
	 * @throws Exception
	 */
	public static BorrowCostModel buildBorrowCost(BorrowModel borrowModel)
			throws Exception {
		BorrowCostModel borrowCostModel = new BorrowCostModel();
		borrowCostModel.setManage(BorrowUtil.getManageFee());
		borrowCostModel.setServices(BorrowUtil.getServicesFee(borrowModel
				.getLimit()));
		borrowCostModel.setDanbao(0d);
		borrowCostModel.setEntrust(0d);
		borrowCostModel.setRealaddress(0d);
		return borrowCostModel;
	}

	/**
	 * 获取借款项目的字典属性配置
	 * 
	 * @return
	 */
	public static List<DictFieldModel> dictFieldModels() {
		List<DictFieldModel> dictFieldModels = new ArrayList<DictFieldModel>();
		dictFieldModels.add(new DictFieldModel("qclass",
				DictTypeEnum.project_type));
		dictFieldModels.add(new DictFieldModel("limit",
				DictTypeEnum.project_term));
		dictFieldModels.add(new DictFieldModel("mode",
				DictTypeEnum.project_return_type));
		dictFieldModels.add(new DictFieldModel("danbao",
				DictTypeEnum.project_danbao));
		dictFieldModels.add(new DictFieldModel("yongtu",
				DictTypeEnum.project_yongtu));
		return dictFieldModels;
	}

	/**
	 * 标的view数据渲染借款信息
	 * 
	 * @param model
	 *            借款对象
	 * @return 渲染以后的借款数据
	 */
	public static BorrowModel borrowViewDispose(BorrowModel model) {
		if (BorrowStatusEnum.in_bid.getStatus().equals(model.getStatus())) {
			// 判断是否已经开始投标
			model.getStrMap().put("isStartBid",
					model.getStarttime() < FrameUtil.getTime(null));
			// 计算投标剩余倒计时时间
			if (model.getStarttime() < FrameUtil.getTime(null)) {
				model.getStrMap().put(
						"blanceInBidTime",
						model.getBiaoqi() * DateUtil.DAY_IN_SECOND
								+ model.getStarttime()
								- FrameUtil.getTime(null));
			} else {
				// 计算开始投标倒计时时间
				Long blanceStartBidTime = model.getStarttime()
						- FrameUtil.getTime(null);
				if (blanceStartBidTime <= 0) {
					blanceStartBidTime = 0L;
				}
				model.getStrMap().put("blanceStartBidTime", blanceStartBidTime);
			}
		}
		return model;
	}

	/**
	 * 获取逾期信息
	 * 
	 * @param model
	 *            还款对象
	 * @param overduePartEnum
	 *            逾期信息查看方
	 * @param curTime
	 *            当时时间时间戳
	 * @return
	 * @throws Exception
	 */
	public static OverdueDto getOverDueDto(ArrearsFormModel model,
			OverduePartEnum overduePartEnum, Long curTime) throws Exception {
		// 对于投资者来说
		if (OverduePartEnum.invest == overduePartEnum) {
			if (ReturnStatusEnum.YES.getValue().equals(model.getStatus())) {
				if (BorrowReturnOverdueAdvanceEnum.YES.getValue().equals(
						model.getAdvance())) {
					return BorrowUtil.buildOverdueDto(model,
							model.getAdvancetime());
				} else {
					return BorrowUtil.buildOverdueDto(model, model.getUptime());
				}
			} else {
				if (BorrowReturnOverdueAdvanceEnum.YES.getValue().equals(
						model.getAdvance())) {
					return BorrowUtil.buildOverdueDto(model,
							model.getAdvancetime());
				} else {
					return BorrowUtil.buildOverdueDto(model, curTime);
				}
			}
		} else {
			if (ReturnStatusEnum.YES.getValue().equals(model.getStatus())) {
				return new OverdueDto(model.getOverdue_day(), model.getOverdue_price(), model.getOverdue_manage());
			} else {
				return BorrowUtil.buildOverdueDto(model, curTime);
			}
		}
	}

	/**
	 * 实际还款日期，返回0表示未知实际还款日期
	 * 
	 * @param model
	 *            借款还款报表
	 * @param overduePartEnum
	 *            逾期信息查看方
	 * @return
	 */
	public static Long getActualReturnTime(ArrearsFormModel model,
			OverduePartEnum overduePartEnum) {
		// 对于投资者来说
		if (OverduePartEnum.invest == overduePartEnum) {
			if (ReturnStatusEnum.YES.getValue().equals(model.getStatus())) {
				if (BorrowReturnOverdueAdvanceEnum.YES.getValue().equals(
						model.getAdvance())) {
					return model.getAdvancetime();
				} else {
					return model.getUptime();
				}
			} else {
				if (BorrowReturnOverdueAdvanceEnum.YES.getValue().equals(
						model.getAdvance())) {
					return model.getAdvancetime();
				} else {
					return 0L;
				}
			}
		} else {
			return model.getUptime();
		}
	}

	/**
	 * 构建罚款费用
	 * 
	 * @param model
	 *            借款还款报表
	 * @param curTime
	 *            当前日期时间戳
	 * @return
	 * @throws Exception
	 */
	public static OverdueDto buildOverdueDto(ArrearsFormModel model,
			Long curTime) throws Exception {
		return buildOverdueDto(new OverdueDtoCalcRquest(model.getCapital(),
				model.getInterest(), model.getManage(), model.getReday(),
				curTime));
	}

	/**
	 * 构建罚款费用
	 * 
	 * @param returnPrice
	 *            应还款金额
	 * @param returnTime
	 *            应还款时间
	 * @param actualReturnTime
	 *            实际时间
	 * @return
	 * @throws Exception
	 */
	public static OverdueDto buildOverdueDto(OverdueDtoCalcRquest ocr)
			throws Exception {
		Long dayCurTime = FrameUtil.getTime(DateUtil.parseDate(DateUtil.format(
				DateUtil.timestampToDate(ocr.getActualReturnTime()),
				DateUtil.PATTERN_yyyyMMdd), DateUtil.PATTERN_yyyyMMdd));
		if (ocr.getReturnTime() >= dayCurTime) {
			return null;
		}
		OverdueDto overdueDto = new OverdueDto();
		// 逾期天数
		overdueDto.setOverdueDay(Integer.valueOf(DateUtil
				.secodsToDay(dayCurTime - ocr.getReturnTime()) + ""));
		if (ocr.getCapital() != null && ocr.getInterest() != null) {
			// 逾期罚息(罚息总额 = 逾期本息总额×对应罚息利率×逾期天数)
			overdueDto.setFinePrice(FeeCalcUtil.round(ArithUtil.muls(ArithUtil
					.add(ocr.getCapital(), ocr.getInterest()), BorrowUtil
					.getOverdueFinePriceFee(overdueDto.getOverdueDay()),
					overdueDto.getOverdueDay(), 0.01)));
		}
		// 逾期罚款管理(逾期管理费总额 = 逾期本息总额×对应逾期管理费率×逾期天数)
		if (ocr.getManage() != null) {
			overdueDto.setFineManagePrice(FeeCalcUtil.round(ArithUtil.muls(ocr
					.getManage(), BorrowUtil.getOverdueFineManageFee(overdueDto
					.getOverdueDay()), overdueDto.getOverdueDay(), 0.01)));
		}
		return overdueDto;
	}

	/**
	 * 根据某个用户在某个标中的投标记录获取回款信息
	 * 
	 * @param investModels
	 *            投资列表
	 * @param borrowModel
	 *            借款对象
	 * @param curTime
	 *            当前日期时间戳（秒）
	 * @return
	 */
	public static ReturnFeeDto buildReturnFeeDtoByInvestor(
			List<InvestModel> investModels, BorrowModel borrowModel,
			Long curTime) {
		double capital = 0;
		for (InvestModel investModel : investModels) {
			capital = ArithUtil.add(capital, investModel.getPrice());
		}
		return FeeCalcUtil.count(
				CalcModeEnum.getCalcModeEnum(borrowModel.getMode()), capital,
				borrowModel.getApr(), borrowModel.getLimit(), curTime);
	}

	/**
	 * 获取领导手机号码列表
	 * 
	 * @return
	 * @throws Exception
	 */
	public static List<AdminMobileDto> getAdminMobileDtos() throws Exception {
		List<AdminMobileDto> list = new ArrayList<AdminMobileDto>();
		String value = SysParamUtil.getValue(SysParamKeyEnum.ADMIN_MOBILE_LIST);
		if (StringUtil.isNotEmpty(value)) {
			String ams[] = value.split(";");
			for (String am : ams) {
				String amtemp[] = am.split("\\|");
				list.add(new AdminMobileDto(amtemp[0], amtemp[1]));
			}
		}
		return list;
	}

	/**
	 * 获取垫付需要通知的领导手机号码列表
	 * 
	 * @return
	 * @throws Exception
	 */
	public static List<AdminMobileDto> getAutoAdvanceAdminMobileDtos()
			throws Exception {
		List<AdminMobileDto> list = new ArrayList<AdminMobileDto>();
		String value = SysParamUtil
				.getValue(SysParamKeyEnum.AUTO_ADVANCE_ADMIN_MOBILE_LIST);
		if (StringUtil.isNotEmpty(value)) {
			String ams[] = value.split(";");
			for (String am : ams) {
				String amtemp[] = am.split("\\|");
				list.add(new AdminMobileDto(amtemp[0], amtemp[1]));
			}
		}
		return list;
	}
	
	static Comparator<ProfitFormModel> proFitFormComparator = new Comparator<ProfitFormModel>() {
		@Override
		public int compare(ProfitFormModel o1, ProfitFormModel o2) {
			return o1.getLimit()-o2.getLimit();
		}
	};
	
	/**
	* 对profitform进行升序排序
	* @param list
	 */
	public static void proFitFormSortAsc(List<ProfitFormModel> list){
		if(list!=null){
			Collections.sort(list, proFitFormComparator);
		}
	}

	/**
	 * 获取债权是否可以转让,返回的map中包含code和message信息，code为ok表示可以转让，{code:"状态码",message:
	 * "提示信息"}
	 * 
	 * @param creditorModel
	 *            债权对象
	 * @param profitFormModels
	 *            投资回款报表
	 * @param transferlimitday
	 *            多少天之后可以转让
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public static Map isTransfer(BorrowModel borrowModel,
			CreditorModel creditorModel,
			List<ProfitFormModel> profitFormModels, long curtime,
			int transferlimitday) throws Exception {
		proFitFormSortAsc(profitFormModels);
		if (borrowModel == null || creditorModel == null
				|| profitFormModels == null || profitFormModels.size() == 0) {
			return FrameUtil.newHashMap("code", "error", "message", "参数有误!");
		}
		if (!borrowModel.getStatus().equals(
				BorrowStatusEnum.return_bid.getStatus())) {
			return FrameUtil.newHashMap("code", "error", "message", "标的不在还款中!");
		}

		if (!CreditorStatusEnum.effective.getValue().equals(
				creditorModel.getStatus())) {
			return FrameUtil.newHashMap("code", "error", "message", "债权状态不对!");
		}
		//2015-04-01修改
		// if ((creditorModel.getReward() != null && creditorModel.getReward() >
		// 0)
		// || (creditorModel.getVolume() != null && creditorModel
		// .getVolume() > 0)) {
		// return FrameUtil.newHashMap("code", "error", "message",
		// "该债券中使用过红包或者充值券，不能够转让!");
		// }
		
		//总份数
		int transferTotalNum = BorrowUtil.transferTotalNum(creditorModel,
				borrowModel.getEach_price());
		// 不可转让份数
		int noTransferTotalNum = BorrowUtil.noTransferTotalNum(creditorModel,
				borrowModel.getEach_price());
		
		if(transferTotalNum == noTransferTotalNum){
			return FrameUtil.newHashMap("code", "error", "message", "该债权包含红包或代金券不可转让!");
		}
		
		// 本期
		ProfitFormModel curpf = BorrowUtil.getTimeInProfitForm(
				profitFormModels, curtime);
		// 当期不为空
		if (curpf == null) {
			return FrameUtil.newHashMap("code", "error", "message", "参数有误!");
		}
		// 上期
		ProfitFormModel prepf = BorrowUtil.getPreProfitForm(profitFormModels,
				curpf);
		// 当前时间小于最小转让时间
		long cantransfertime = creditorModel.getStarttime()
				+ (transferlimitday * DateUtil.DAY_IN_SECOND);
		if (cantransfertime >= curtime) {
			return FrameUtil.newHashMap(
					"code",
					"error",
					"message",
					DateUtil.timestampToDateString(cantransfertime,
							DateUtil.PATTERN_yyyy_MM_dd) + " 后方可进行转让!");
		}
		// 最后一期还款日当天不可转让
		Long curtimeMYD = FrameUtil.getTime(DateUtil.parseDate(DateUtil
				.timestampToDateString1(curtime, DateUtil.PATTERN_yyyy_MM_dd),
				DateUtil.PATTERN_yyyy_MM_dd));
		if (curpf.getLimit().equals(borrowModel.getLimit())
				&& curpf.getReday().equals(curtimeMYD)) {
			return FrameUtil.newHashMap("code", "error", "message",
					"最后一期还款日当天不可转让。");
		}
		// 如果上期不为空，判断上期是否未还款，如果未还款不能转让
		if (prepf != null) {
			if (prepf.getStatus().equals(ReturnStatusEnum.NO.getValue())) {
				return FrameUtil.newHashMap("code", "error", "message",
						"上期还款处于逾期状态，不能转让!");
			}
		}
		return FrameUtil.newHashMap("code", "ok");
	}

	/**
	 * 计算最多可转让几份
	 * 
	 * @param creditorModel
	 *            债权详情
	 * @param pricing
	 *            每份的单价
	 * @param bool 
	 * 			  是否计算红包(false:不计算红包;true:减去红包占用的数量)
	 * @return int
	 */
	public static int transferTotalNum(CreditorModel creditorModel,
			Double pricing) {
		if (creditorModel == null || pricing == null
				|| pricing.doubleValue() <= 0) {
			FrameUtil.throwBaseException1("参数异常");
		}
		// 总份数
		return Double.valueOf(
				ArithUtil.div(creditorModel.getInvest(), pricing)).intValue();
	}
	
	/**
	 * 债权中不可转出部分
	* @param creditorModel 债权信息
	* @param pricing 单价
	* @return int 
	 */
	public static int noTransferTotalNum(CreditorModel creditorModel,
			Double pricing){
		if (creditorModel == null || pricing == null
				|| pricing.doubleValue() <= 0) {
			FrameUtil.throwBaseException1("参数异常");
		}
		Double reward = creditorModel.getReward(), volume = creditorModel
				.getVolume();
		int num = 0;
		// 计算红包占用的份数
		if (reward != null && reward > 0) {
			int rewardNum = Double.valueOf(
					ArithUtil.div(reward, pricing, 0, BigDecimal.ROUND_UP))
					.intValue();
			num += ((rewardNum == 0) ? 1 : rewardNum);
		}

		// 代金券占用的份数
		if (volume != null && volume > 0) {
			int volumeNum = Double.valueOf(
					ArithUtil.div(volume, pricing, 0, BigDecimal.ROUND_UP))
					.intValue();
			num += ((volumeNum == 0) ? 1 : volumeNum);
		}
		return num;
	}
	
	

	/**
	 * 获取curtime在会还报表的第几期
	 * 
	 * @param profitFormModels
	 *            投资回款列表
	 * @param curtime
	 *            当前日期时间戳
	 * @throws Exception
	 */
	public static ProfitFormModel getTimeInProfitForm(
			List<ProfitFormModel> profitFormModels, long curtime)
			throws Exception {
		for (ProfitFormModel profitFormModel : profitFormModels) {
			if (DateUtil.getTimestampToTimestamp(curtime,
					DateUtil.PATTERN_yyyy_MM_dd) <= profitFormModel.getReday()) {
				return profitFormModel;
			}
		}
		return null;
	}

	/**
	 * 获取curpf的上期回款报表
	 * 
	 * @param profitFormModels
	 *            投资回款报表列表
	 * @param curpf
	 *            当前回款对象
	 * @return
	 * @throws Exception
	 */
	public static ProfitFormModel getPreProfitForm(
			List<ProfitFormModel> profitFormModels, ProfitFormModel curpf)
			throws Exception {
		for (ProfitFormModel profitFormModel : profitFormModels) {
			if (profitFormModel.getLimit().equals(curpf.getLimit() - 1)) {
				return profitFormModel;
			}
		}
		return null;
	}

	/**
	 * 获取债权多少天之后可以转让
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Integer getCreditorTransferBeforeDay() throws Exception {
		SysParamModel sp = SysParamUtil
				.getSysParamModel(SysParamKeyEnum.CREDITOR_TRANSFER_BEFORE_DAY);
		if (sp == null) {
			logger.error("系统参数CREDITOR_TRANSFER_BEFORE_DAY不存在");
			FrameUtil.throwBaseException1("系统参数[债权生效多少天之后可以转让]不存在!");
		}
		return Integer.valueOf(sp.getParam_value());
	}

	/**
	 * 债权转让有效天数
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Integer getCreditorTransferTimelimit() throws Exception {
		SysParamModel sp = SysParamUtil
				.getSysParamModel(SysParamKeyEnum.CREDITOR_TRANSFER_TIMELIMIT);
		if (sp == null) {
			logger.error("系统参数CREDITOR_TRANSFER_TIMELIMIT不存在");
			FrameUtil.throwBaseException1("系统参数[债权转让有效天数]不存在!");
		}
		return Integer.valueOf(sp.getParam_value());
	}

	/**
	 * 获取债权转让费率
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Double getCreditorTransferFee() throws Exception {
		SysParamModel sp = SysParamUtil
				.getSysParamModel(SysParamKeyEnum.CREDITOR_TRANSFER_FEE);
		if (sp == null) {
			logger.error("系统参数CREDITOR_TRANSFER_FEE不存在");
			FrameUtil.throwBaseException1("债权转让费率不存在!");
		}
		return Double.valueOf(sp.getParam_value());
	}

	/**
	 * 根据传入的债权信息，标的信息 算出债权价值 折让后的年利率
	 * 
	 * @param borrowModel
	 *            借款对象
	 * @param creditorModel
	 *            债权对象
	 * @param curtime
	 *            当前时间
	 * @param parameters
	 *            折让参数 本息保障为1 本金保障为0
	 * @return
	 * @throws Exception
	 */
	public static CreditorTransferCounterDto getCreditorTransferCounter(
			BorrowModel borrowModel, CreditorModel creditorModel, long curtime,
			int parameters) throws Exception {
		List<ProfitFormModel> profitFormModels = buildProfitFormModels(
				borrowModel, creditorModel);
		// 折让金额
		double discount = ArithUtil.muls(creditorModel.getCapital(),
				BorrowUtil.getCreditorTransferFee(), 0.01);
		// 利息补偿天数
		int interestDays = 0;
		// 债权状态类型(1.本期未还，2.本期已还，下期未还或者没有，3.本期以及下面N其已还)
		int creditorType = 0;
		// 月利率
		double monthApr = ArithUtil.mul(
				ArithUtil.div(borrowModel.getApr(), 12), 0.01);

		Map<Integer, ProfitFormModel> ermap = new HashMap<Integer, ProfitFormModel>();
		// 当前年月日
		long curtime_ymd = DateUtil.getTimestampToTimestamp(curtime,
				DateUtil.PATTERN_yyyy_MM_dd);
		// 上期，本期，下期,所有已还的期数中最大的一期
		ProfitFormModel prePf = null, curPf = null, nextPf = null, lastRePf = null;
		for (ProfitFormModel pf : profitFormModels) {
			if (curPf == null && curtime_ymd <= pf.getReday()) {
				curPf = pf;
			}
			ermap.put(pf.getLimit(), pf);
			if (pf.getStatus().equals(ReturnStatusEnum.YES.getValue())) {
				lastRePf = pf;
			}
		}
		if (curPf == null) {
			return null;
		}
		prePf = ermap.get(curPf.getLimit() - 1);
		nextPf = ermap.get(curPf.getLimit() + 1);
		// 本期未还
		if (curPf.getStatus().equals(ReturnStatusEnum.NO.getValue())) {
			creditorType = 1;
		} else {
			if (nextPf != null) {
				if (nextPf.getStatus().equals(ReturnStatusEnum.NO.getValue())) {
					// 本期已还，下期未还
					creditorType = 2;
				} else {
					// 当期且下面N期已还，但未还清
					creditorType = 3;
				}
			} else {
				// 本期已还，下期没有
				creditorType = 2;
			}
		}
		CreditorTransferCounterDto cto = new CreditorTransferCounterDto(0d, 0d,
				creditorType, 0, 0d, 0, 0d);
		switch (creditorType) {
		case 1:
			// 本期未还
			interestDays = Math.min(DateUtil.getTimeStempBetweenDay(
					prePf != null ? prePf.getReday() : creditorModel
							.getStarttime(), curtime), 30);
			cto.setCreditorValue(ArithUtil.add(creditorModel.getCapital(),
					ArithUtil.muls(ArithUtil.div(interestDays, 30),
							creditorModel.getCapital(), monthApr, parameters)));
			break;
		case 2:
			// 本期已还，下期未还或者不存在
			interestDays = Math.min(
					DateUtil.getTimeStempBetweenDay(curtime, curPf.getReday()),
					30);
			cto.setCreditorValue(ArithUtil.sub(creditorModel.getCapital(),
					ArithUtil.muls(ArithUtil.div(interestDays, 30),
							creditorModel.getCapital(), monthApr, parameters)));
			break;
		case 3:
			// 3.本期以及下面N其已还
			interestDays = Math.min(
					DateUtil.getTimeStempBetweenDay(curtime, curPf.getReday()),
					30);
			int pluslimit = lastRePf.getLimit() - curPf.getLimit();
			double temp1 = ArithUtil.pow(ArithUtil.add(1, monthApr), pluslimit);
			double temp = ArithUtil.div(creditorModel.getCapital(), temp1);
			cto.setCreditorValue(ArithUtil.sub(
					temp,
					ArithUtil.mul(ArithUtil.div(
							ArithUtil.mul(temp, interestDays), 30), monthApr)));
			break;
		default:
			break;
		}
		// 获取剩余天数
		cto.setInterestDays(interestDays);
		cto.setDiscount(FeeCalcUtil.round(discount));
		cto.setCreditorDaysSurplus(DateUtil.getTimeStempBetweenDay(curtime,
				ermap.get(ermap.size()).getReday()));
		cto.setCreditorValue(FeeCalcUtil.round(cto.getCreditorValue()));
		cto.setCreditorBuyPrice(FeeCalcUtil.round(ArithUtil.sub(
				cto.getCreditorValue(), discount)));
		if (cto.getCreditorBuyPrice() <= 0) {
			logger.error("债权转让有误:" + FrameUtil.json(creditorModel)
					+ ",curtime:" + curtime);
			FrameUtil.throwBaseException1("债权转让有误!");
		}
		cto.setApr(FeeCalcUtil.round(ArithUtil.muls(ArithUtil.divs(
				ArithUtil.sub(creditorModel.getRecovery(),
						cto.getCreditorBuyPrice()),
				cto.getCreditorBuyPrice(),
				(cto.getCreditorDaysSurplus() == 0 ? 1 : cto
						.getCreditorDaysSurplus())), 365, 100)));
		return cto;
	}

	/**
	 * 构建投资人回款报表
	 * 
	 * @param borrowModel
	 *            标的信息
	 * @param creditorModel
	 *            债权信息
	 * @return
	 * @throws Exception 
	 */
	public static List<ProfitFormModel> buildProfitFormModels(
			BorrowModel borrowModel, CreditorModel creditorModel) throws Exception {
		List<ProfitFormModel> profitFormModels = new ArrayList<ProfitFormModel>();
		ReturnFeeDto returnFeeDto = FeeCalcUtil.count(
				CalcModeEnum.getCalcModeEnum(borrowModel.getMode()),
				creditorModel.getCapital(), borrowModel.getApr(),
				borrowModel.getLimit(), borrowModel.getRepay_starttime());
		List<EveryReturnFeeDto> list = returnFeeDto.getData();
		int limit = 0;
		for (EveryReturnFeeDto everyReturnFeeDto : list) {
			limit++;
			ProfitFormModel profitFormModel = new ProfitFormModel(
					borrowModel.getBorrow_id(),
					creditorModel.getUser_id(),
					limit,
					everyReturnFeeDto.getPrice(),
					everyReturnFeeDto.getCapital(),
					everyReturnFeeDto.getInterest(),
					everyReturnFeeDto.getReday(),
					borrowModel.getRepayday() > everyReturnFeeDto.getReday() ? ReturnStatusEnum.YES
							.getValue() : ReturnStatusEnum.NO.getValue(), 0l,
					0l, creditorModel.getCreditor_id());
			profitFormModels.add(profitFormModel);
		}
		return profitFormModels;
	}

	/**
	 * 获取某个会员还可以投入的金额
	 * 
	 * @param borrowModel
	 *            借款对象
	 * @param fundsModel
	 *            会员的资金对象
	 * @param investModels
	 *            投资对象列表
	 * @return
	 * @throws Exception
	 */
	public static double getUserInvestInBorrow(BorrowModel borrowModel,
			List<InvestModel> investModels) throws Exception {
		// 标的剩余额度
		double blanceInvest = ArithUtil.sub(borrowModel.getPrice(),
				borrowModel.getPrice_invest());
		// 最高可投标金额
		double investMax = borrowModel.getPrice_invest_max();
		if (borrowModel.getPrice_invest_max() <= 0) {
			return blanceInvest;
		}
		// 在标中目前投入的金额
		double investTotalInBorrow = 0;
		if (investModels.size() >= 1) {
			for (InvestModel investModel : investModels) {
				investTotalInBorrow = ArithUtil.add(investTotalInBorrow,
						investModel.getPrice());
			}
		}
		// 每份投标金额
		double investEachAmount = borrowModel.getEach_price();
		// 用户还可以投入的金额
		double userCanInvest = 0;
		if (investMax > investTotalInBorrow) {
			userCanInvest = ArithUtil.sub(investMax, investTotalInBorrow);
			if (userCanInvest < blanceInvest) {
				return ArithUtil.sub(userCanInvest,
						ArithUtil.mod(userCanInvest, investEachAmount));
			} else if (userCanInvest >= blanceInvest) {
				return blanceInvest;
			}
		}
		return 0d;
	}

	/**
	 * 获取某个用户的投资总额
	 * 
	 * @param investModels
	 *            某个用户的投资列表
	 * @param user_id
	 *            某个用户的id
	 * @return
	 */
	public static double getUserInvestTotal(List<InvestModel> investModels,
			long user_id) {
		double result = 0;
		if (investModels != null) {
			for (InvestModel investModel : investModels) {
				if (investModel.getUser_id() != null
						&& investModel.getUser_id().equals(user_id)) {
					result = ArithUtil.add(result, investModel.getPrice());
				}
			}
		}
		return result;
	}
	
	/**
	* 获取借款人数据
	* @param borrowDataModel
	* @param borrowModel
	* @param userDataService
	* @return
	* @throws Exception
	 */
	public static UserDataModel getUserData(BorrowDataModel borrowDataModel,
			BorrowModel borrowModel,IUserDataService userDataService) throws Exception {
		UserDataModel userDataModel;
		if (borrowDataModel != null
				&& StringUtil.isNotEmpty(borrowDataModel.getUserdata())) {
			userDataModel = UserDataModelConvert.convert(JSON.parseObject(
					borrowDataModel.getUserdata(), UserDataModel.class));
		} else {
			userDataModel = UserDataModelConvert
					.convert((UserDataModel) userDataService.getModelById(
							borrowModel.getUser_id(), DbWREnums.READ));
		}
		return userDataModel;
	}
	
	/**
	 * @param afm 根据标的回款报表和投资回款报表计算标的逾期信息，按照投资人来算
	 * @param curtime
	 * @param profitFormModels
	 * @return
	 * @throws Exception
	 */
	public static OverdueDto getRepayOverdue(ArrearsFormModel afm, long curtime,
			List<ProfitFormModel> profitFormModels) throws Exception {
		OverdueDto odt = BorrowUtil.getOverDueDto(afm, OverduePartEnum.borrow,
				curtime);
		if (odt == null) {
			return null;
		}else if (ReturnStatusEnum.YES.getValue().equals(afm.getStatus())) {
			return odt;
		}
		if (profitFormModels != null && profitFormModels.size() >= 1) {
			odt.setFinePrice(0d);
			for (ProfitFormModel v : profitFormModels) {
				OverdueDto od = BorrowUtil
						.buildOverdueDto(new OverdueDtoCalcRquest(v
								.getCapital(), v.getInterest(), null, v
								.getReday(), curtime));
				if (od == null) {
					return null;
				}
				odt.setFinePrice(ArithUtil.add(odt.getFinePrice(),
						od.getFinePrice()));
			}
		}
		return odt;
	}

	public static void main(String[] args) {
		/*Long curtime = FrameUtil.getTime(null);
		Long endtime = FrameUtil.getTime(DateUtil.parseDate(
				"2015-06-22 00:00:00", DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss));
		System.out.println(DateUtil.getTimeStempBetweenDay(curtime, endtime));*/
		BorrowModel borrowModel = new BorrowModel();
		borrowModel.setBorrow_id(4909L);
		borrowModel.setMode(2);
		borrowModel.setApr(10.00);
		borrowModel.setLimit(6);
		borrowModel.setRepay_starttime(1442916314L);
		borrowModel.setUser_id(278646L);
		borrowModel.setRepayday(1458576000L);
		CreditorModel creditorModel = new CreditorModel();
		creditorModel.setCapital(49700D);
		creditorModel.setStarttime(1447894348L);
		creditorModel.setRecovery(50114.16D);
		creditorModel.setUser_id(251100L);
		creditorModel.setCreditor_id(192296L);
		long curtime = 1456274690L;
		int parameters = 1;
		try {
			CreditorTransferCounterDto dto = BorrowUtil.getCreditorTransferCounter(borrowModel, creditorModel, curtime, parameters);
			System.out.println(dto);
			System.out.println(FrameUtil.json(dto, true));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
